#include "View.h"
#include <SEngine/Render/BaseRenderer.h>
#include <SEEdit/Render/RenderFeature/DebugRenderFeature.h>
#include <SEngine/Render/RenderFeature/FrameRenderFeature.h>
#include <SEngine/Render/RenderFeature/UBORenderFeature.h>
#include <SEngine/Render/RenderFeature/TextRenderFeature.h>
#include <SERender/Descriptors/FrameDescriptor.h>
#include <SEEdit/Render/RenderPass/SelectModelRenderPass.h>
#include <SEUi/SEUi.h>
#include <SEEdit/Layer/EditLayer.h>
#include <SEngine/Render/Descriptors/SceneDescriptor.h>
#include <SEEdit/Descriptors/SelectActorDescriptor.h>
#include <SEngine/Ecs/Components/UuidComp.h>
#include <SEngine/Ecs/Components/TransformComp.h>
#include <SECore/Ecs/Scene.h>
#include <SECore/CoreApplication.h>
#include <SEngine/Project/Project.h>
#include <SEEdit/Descriptors/ControlBarStateDescriptor.h>

#include <SERender/Resourses/Loader/TextureLoader.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/matrix_decompose.hpp>
#include <SEngine/Util/RenderPassGUIHandler.h>
#include <SEEdit/Render/RenderPass/GizmoRenderPass.h>
#include <SEEdit/Render/RenderPass/GridRenderPass.h>
#include <SEEdit/Render/RenderPass/TestRenderPass.h>
#include <SEngine/Render/RenderPass/SkyboxRenderPass.h>

#include <SECore/Events/Input.h>
#include <SEDebug/Log/Log.h>

#include <SECore/PluginManager.h>
#include <SECore/Config.h>
#include <SECore/Manager.h>

namespace SEngine
{
    View::View(EditLayer & layer)
        : Widget()
        , m_layer(layer)
    {
        TextureLoader<Texture2D> texLoader;
        texLoader.SetInternalFormat(RGB);
        texLoader.SetFormat(RGB);
        texLoader.SetType(UNSIGNED_BYTE);
        texLoader.SetParameter(TEXTURE_MAG_FILTER, LINEAR);
        texLoader.SetParameter(TEXTURE_MIN_FILTER, LINEAR);
        texLoader.SetParameter(TEXTURE_WRAP_S, CLAMP_TO_EDGE);
        texLoader.SetParameter(TEXTURE_WRAP_T, CLAMP_TO_EDGE);
        m_targetMap = texLoader.CreateTexture(800, 600, nullptr);
        m_fbo = std::make_unique<FrameBuffer>();
        m_fbo->SwapAttachments(Attachment::COLOR_ATTACHMENT0, *m_targetMap);


        m_renderer = std::make_unique<BaseRenderer>(std::make_unique<CurrentDrive>());

        m_renderer->Descriptor<RenderFeature>::AddDescriptor(std::make_unique<DebugRenderFeature>(*m_renderer));
        

        
        m_renderer->Descriptor<RenderPass>::AddDescriptor(std::make_unique<SkyboxRenderPass>(*m_renderer));
        m_renderer->Descriptor<RenderPass>::AddDescriptor(std::make_unique<GridRenderPass>(*m_renderer));
        m_renderer->Descriptor<RenderPass>::AddDescriptor(std::make_unique<TestRenderPass>(*m_renderer));
        
        m_renderPipline = PluginManager(Manager::GetInstance().Get<ConfigNode>("SEConfig").value().Value()).CreatePlugin(m_renderer.get());
        if (m_renderPipline) {
            Log::GetInstance().Info("Load Pipline: {}", m_renderPipline->Name());
        } else {
            Log::GetInstance().Warn("Pipline error");
        }
        
        m_renderer->Descriptor<RenderPass>::AddDescriptor(std::make_unique<GizmoRenderPass>(*m_renderer));
        m_renderer->Descriptor<RenderPass>::AddDescriptor(std::make_unique<SelectModelRenderPass>(*m_renderer));
        
        m_renderer->Descriptor<RenderPass>::Foreach([this](RenderPass & pass) {
            if (auto handler = dynamic_cast<RenderPassGUIHandler*>(&pass); handler) {
                SignalUpdateRenderPassSettingUI.AddSlot([this, handler]() {
                    handler->SignalUpdateUI.Invoke();
                    return SlotState::Keep;
                });
            }
        });
        m_renderer->SetFrameBuffer(*m_fbo);

        auto project = m_layer.GetApp().GetDescriptor<Project>();
        if (project) {
            m_renderer->AnyDescriptor::AddDescriptor<SceneDescriptor>(std::move(SceneDescriptor{project->get().GetScene()}));
        }

        this->SetWindowFlag(ImGuiWindowFlags_MenuBar, true);
    }

    void View::SetSelectActorByID(uint32_t id)
    {
        auto project = m_layer.GetApp().GetDescriptor<Project>();
        if (!project) {
            return;
        }
        auto selectActorDes = m_layer.GetApp().GetDescriptor<SelectActorDescriptor>();
        if (!selectActorDes) {
            return;
        }
        auto scene = project->get().GetScene();
        selectActorDes->get().selectActor = std::nullopt;
        scene->Components<UuidComp>([&](Actor& actor, UuidComp & uuid) {
            if (uuid.Hash<uint32_t>() == id) {
                selectActorDes->get().selectActor = actor;
            }
        });
    }

    void View::SetSelectActorByID(uint8_t id)
    {
        auto project = m_layer.GetApp().GetDescriptor<Project>();
        if (!project) {
            return;
        }
        auto selectActorDes = m_layer.GetApp().GetDescriptor<SelectActorDescriptor>();
        if (!selectActorDes) {
            return;
        }
        auto scene = project->get().GetScene();
        selectActorDes->get().selectActor = std::nullopt;
        scene->Components<UuidComp>([&](Actor& actor, UuidComp & uuid) {
            if (uuid.Hash<uint8_t>() == id) {
                selectActorDes->get().selectActor = actor;
            }
        });
    }

    void View::FocusSelectActor()
    {
        auto selectActorDes = m_layer.GetApp().GetDescriptor<SelectActorDescriptor>();
        if (!selectActorDes || !selectActorDes->get().selectActor) {
            return;
        }
        auto & actor = selectActorDes->get().selectActor->get();
        auto & trans = actor.GetComponent<TransformComp>()->get();
        auto targetPos = glm::vec3{trans.GetModel() * glm::vec4{0.0f, 0.0f, 0.0f, 1.0f}};
        SignalFocusSelectActor.Invoke(targetPos);
    }

    void View::DeleteSelectActor()
    {
        auto & selectActor = m_layer.GetApp().GetDescriptor<SelectActorDescriptor>()->get();
        if (selectActor.selectActor) {
            auto & actor = selectActor.selectActor->get();
            m_layer.DeleteActor(actor);
        }
    }

    void View::BeginUpdate()
    {
        ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {0.0f, 0.0f});
    }
    
    void View::UpdateImpl(int delta)
    {
        ShowMenuBar();
        auto selectActorDes = m_layer.GetApp().GetDescriptor<SelectActorDescriptor>(); 
        if (!selectActorDes) {
            return;
        }
        auto rect = this->GetRect();
        rect.w -= 40;        // 减去titel高度
        m_renderer->AnyDescriptor::AddDescriptor<SelectActorDescriptor>(SelectActorDescriptor{selectActorDes->get()});
        m_renderer->AnyDescriptor::AddDescriptor<FrameDescriptor>(FrameDescriptor{0, 0, rect.z, rect.w, m_editCamera});
        m_renderer->BeginFrame();
        m_renderer->DrawFrame();
        m_renderer->EndFrame();
        ImGui::Image((ImTextureID)(m_targetMap->GetId()), {(float)rect.z, (float)rect.w}, {0.0f, 1.0f}, {1.0f, 0.0f});
        DrawGizmo();
        if (Input::GetInstance().IsMousePressed(MouseButton::SE_RIGHT)) {
            ImGui::SetMouseCursor(ImGuiMouseCursor_None);
        }
    }

    void View::EndUpdate()
    {
        ImGui::PopStyleVar();
        SignalCameraUpdate.Invoke(m_editCamera);
    }

    void View::ShowMenuBar()
    {
        ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {9.0f, 9.0f});
        auto & state = CoreApplication::App().GetDescriptor<ControlBarStateDescriptor>()->get();
        if (ImGui::BeginMenuBar()) {
            if (ImGui::BeginMenu("Setting")) {
                if (ImGui::MenuItem("Translate", "", state.gizmoMode == GizmoMode::Translate)) state.gizmoMode = GizmoMode::Translate;
                if (ImGui::MenuItem("Rotate", "", state.gizmoMode == GizmoMode::Rotate)) state.gizmoMode = GizmoMode::Rotate;
                if (ImGui::MenuItem("Scale", "", state.gizmoMode == GizmoMode::Scale)) state.gizmoMode = GizmoMode::Scale;
                if (ImGui::MenuItem("Universal", "", state.gizmoMode == GizmoMode::Universal)) state.gizmoMode = GizmoMode::Universal;
                ImGui::EndMenu();
            }
            ImGui::EndMenuBar();
        }
        ImGui::PopStyleVar();
    }

    void View::DrawGizmo()
    {
        auto & state = CoreApplication::App().GetDescriptor<ControlBarStateDescriptor>()->get();
        auto rect = this->GetRect();
        rect.w -= 40;        // 减去titel高度
        rect.y += 40;
        auto & selectActor =  CoreApplication::App().GetDescriptor<SelectActorDescriptor>()->get();
        if (selectActor.selectActor) {
            auto & transComp = selectActor.selectActor->get().GetComponent<TransformComp>()->get();
            glm::mat4 model = transComp.GetModel();
            // ImGuizmo::AllowAxisFlip(false);
            ImGuizmo::SetOrthographic(false);
            ImGuizmo::SetDrawlist();
            ImGuizmo::SetRect((float)rect.x, (float)rect.y, (float)rect.z, (float)rect.w);
            ImGuizmo::Manipulate(glm::value_ptr(this->GetCamera().GetViewMatrix()), 
                glm::value_ptr(this->GetCamera().GetProjectMatrix((int)rect.z, (int)rect.w)), 
                (ImGuizmo::OPERATION)state.gizmoMode, ImGuizmo::MODE::LOCAL, 
                glm::value_ptr(model)
            );
            if (ImGuizmo::IsUsing()) {
                transComp.SetModel(model);
            }
        }
    }

} // namespace SEngine

